package com.hongkuncheng.cms.helper;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.hibernate.FactorySession;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.type.StandardBasicTypes;
import org.springframework.stereotype.Component;
import com.hongkuncheng.cms.model.Article;
import com.hongkuncheng.cms.model.WeixinSendrecord;
import com.alibaba.fastjson.*;

/**
 * 微信帮助类
 *
 * @author 洪坤成
 */
@Component
public class WeixinHelper {

    public static FactorySession factorySession;
    public static String type;
    static String appId;
    static String appSecret;
    static String token;
    static String applicationDirectory;

    public static void initial(String type, String appId, String appSecret, String token, String applicationDirectory) {
        WeixinHelper.type = type;
        WeixinHelper.appId = appId;
        WeixinHelper.appSecret = appSecret;
        WeixinHelper.token = token;
        WeixinHelper.applicationDirectory = applicationDirectory;
    }

    //////////////////////////////////////// 回调方法  ////////////////////////////////////////

    // 响应
    public static String response(HttpServletRequest request) throws Exception {
        String echostr = request.getParameter("echostr");
        if (echostr != null && !echostr.isEmpty()) {
            if (checkSignature(request)) {
                return echostr;
            } else {
                return "error";
            }
        } else {
            return responseMsg(request);
        }
    }

    // 自动回复内容
    private static String responseMsg(HttpServletRequest request) throws Exception {
        String postStr = readStreamParameter(request.getInputStream());
        Document document = DocumentHelper.parseText(postStr);
        Element root = document.getRootElement();
        String fromUsername = root.elementText("FromUserName");
        String toUsername = root.elementText("ToUserName");
        String keyword = root.elementTextTrim("Content");
        String msgType = root.elementTextTrim("MsgType");
        String time = new Date().getTime() + "";
        String textTpl = "<xml>" + "<ToUserName><![CDATA[%s]]></ToUserName>"
                + "<FromUserName><![CDATA[%s]]></FromUserName>" + "<CreateTime>%s</CreateTime>"
                + "<MsgType><![CDATA[%s]]></MsgType>" + "<Content><![CDATA[%s]]></Content>" + "<FuncFlag>0</FuncFlag>"
                + "</xml>";
        if (msgType.equals("text")) {
            String contentStr = "收到你的消息。";
            String resultStr = textTpl.format(textTpl, fromUsername, toUsername, time, msgType, contentStr);
            return resultStr;
        } else if (msgType.equals("image")) {
            String contentStr = "这张照片不错哟~";
            String resultStr = textTpl.format(textTpl, fromUsername, toUsername, time, msgType, contentStr);
            return resultStr;
        } else if (msgType.equals("location")) {
            String location_X = root.elementTextTrim("Location_X");
            String location_Y = root.elementTextTrim("Location_Y");
            String contentStr = "你的纬度是北纬：" + location_X + "，你的经度是东经：" + location_Y;
            String resultStr = textTpl.format(textTpl, fromUsername, toUsername, time, msgType, contentStr);
            return resultStr;
        } else if (msgType.equals("voice")) {
            String contentStr = "大声点，听不见";
            String resultStr = textTpl.format(textTpl, fromUsername, toUsername, time, msgType, contentStr);
            return resultStr;
        } else if (msgType.equals("video")) {
            String contentStr = "再来一段~";
            String resultStr = textTpl.format(textTpl, fromUsername, toUsername, time, msgType, contentStr);
            return resultStr;
        } else if (msgType.equals("event")) {
            // String event =
            return null;
        } else {
            String contentStr = "我不知道你在做什么~~";
            String resultStr = textTpl.format(textTpl, fromUsername, toUsername, time, msgType, contentStr);
            return resultStr;
        }

    }

    // 微信接口验证
    private static boolean checkSignature(HttpServletRequest request) {
        String signature = request.getParameter("signature");
        String timestamp = request.getParameter("timestamp");
        String nonce = request.getParameter("nonce");
        String[] tmpArr = {token, timestamp, nonce};
        Arrays.sort(tmpArr);
        String tmpStr = ArrayToString(tmpArr);
        tmpStr = SHA1Encode(tmpStr);
        if (tmpStr.equalsIgnoreCase(signature)) {
            return true;
        } else {
            return false;
        }
    }

    // 数组转字符串
    private static String ArrayToString(String[] arr) {
        StringBuffer bf = new StringBuffer();
        for (int i = 0; i < arr.length; i++) bf.append(arr[i]);
        return bf.toString();
    }

    // sha1加密
    private static String SHA1Encode(String sourceString) {
        String resultString = null;
        try {
            resultString = new String(sourceString);
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            resultString = byte2hexString(md.digest(resultString.getBytes()));
        } catch (Exception ex) {
        }
        return resultString;
    }

    // byte转hex
    private static final String byte2hexString(byte[] bytes) {
        StringBuffer buf = new StringBuffer(bytes.length * 2);
        for (int i = 0; i < bytes.length; i++) {
            if (((int) bytes[i] & 0xff) < 0x10) buf.append("0");
            buf.append(Long.toString((int) bytes[i] & 0xff, 16));
        }
        return buf.toString().toUpperCase();
    }

    // 从输入流读取post参数
    private static String readStreamParameter(ServletInputStream in) {
        StringBuilder buffer = new StringBuilder();
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(in));
            String line = null;
            while ((line = reader.readLine()) != null) buffer.append(line);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != reader) {
                try {
                    reader.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return buffer.toString();
    }

    //////////////////////////////////////// 主动方法  ////////////////////////////////////////

    // 获取Token
    public static String getAccessToken(HttpServletRequest request, HttpServletResponse response) throws JSONException {
        String access_token = null;
        if (request.getSession().getAttribute("weixin_token") != null) {
            access_token = request.getSession().getAttribute("weixin_token").toString();
        } else {
            String json = HttpHelper.get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret);
            JSONObject jsonObject = JSON.parseObject(json);
            access_token = jsonObject.getString("access_token");
            request.getSession().getAttribute("weixin_token");
        }
        return access_token;
    }

    //////////////////////////////////////// 资源上传  ////////////////////////////////////////

    // 上传媒体
    public static String uploadMedia(HttpServletRequest request, HttpServletResponse response, String type, String fileUrl) throws JSONException {
        String uploadMediaUrl = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=" + WeixinHelper.getAccessToken(request, response) + "&type=" + type;
        Map<String, Object> param = new HashMap<String, Object>();
        param.put("media", new File(applicationDirectory + fileUrl));
        String json = HttpHelper.post(uploadMediaUrl, param);
        JSONObject jsonObject = JSON.parseObject(json);
        return jsonObject.getString("media_id");
    }

    // 上传图片
    public static String uploadImage(HttpServletRequest request, HttpServletResponse response, String fileUrl) throws JSONException {
        String uploadMediaUrl = "https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=" + WeixinHelper.getAccessToken(request, response);
        Map<String, Object> param = new HashMap<String, Object>();
        param.put("media", new File(applicationDirectory + fileUrl));
        String json = HttpHelper.post(uploadMediaUrl, param).replace("\\", "");
        JSONObject jsonObject = JSON.parseObject(json);
        return jsonObject.getString("url");
    }

    // 上传Mp图文
    public static String uploadMpNews(HttpServletRequest request, HttpServletResponse response, List<Article> articles) throws JSONException {
        StringBuilder sbMpnewses = new StringBuilder();
        Pattern p = Pattern.compile("<img[^>]+src\\s*=\\s*['\"]([^'\"]+)['\"][^>]*>");
        for (Article article : articles) {
            String content = article.getContents();
            Matcher m = p.matcher(content);
            while (m.find()) content = content.replace(m.group(1), uploadImage(request, response, m.group(1)));
            content = content.replace("\"", "'").replace("src='/upload/videos/", "src='" + HttpHelper.getHost(request) + "/upload/videos/");
            sbMpnewses.append(",{\"thumb_media_id\":\"" + uploadMedia(request, response, "thumb", article.getImage()) + "\",\"author\":\"" + article.getAuthor() + "\",\"title\":\"" + article.getTitle() + "\",\"content\":\"" + content + "\",\"digest\":\"" + article.getDigest() + "\",\"show_cover_pic\":\"0\"}");
        }
        if (sbMpnewses.length() > 0) sbMpnewses.deleteCharAt(0);
        String uploadMpNewsUrl = "https://api.weixin.qq.com/cgi-bin/material/add_news?access_token=" + WeixinHelper.getAccessToken(request, response);
        String param = "{\"articles\":[" + sbMpnewses + "]}";
        String json = HttpHelper.post(uploadMpNewsUrl, param);
        JSONObject jsonObject = JSON.parseObject(json);
        return jsonObject.getString("media_id");
    }

    // 发送Mp图文
    public static int sendMpNews(HttpServletRequest request, HttpServletResponse response, List<Article> articles) throws JSONException {
        String media_id = WeixinHelper.uploadMpNews(request, response, articles);
        String url = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token=" + WeixinHelper.getAccessToken(request, response);
        String param = "{\"filter\":{\"is_to_all\":true},\"mpnews\":{\"media_id\":\"" + media_id + "\"},\"msgtype\":\"mpnews\"}";
        String json = HttpHelper.post(url, param);
        JSONObject jsonObject = JSON.parseObject(json);
        return jsonObject.getIntValue("errcode");
    }

    //////////////////////////////////////// 其它方法  ////////////////////////////////////////

    // 获取剩余群发条数
    public static boolean getSurplus(StringBuilder msg) {
        boolean result = false;
        Date date = new Date();
        if (type.equals("dingyue")) {
            // 订阅号
            int count = Integer.parseInt(factorySession.createCriteria(WeixinSendrecord.class)
                    .add(Restrictions.sqlRestriction("date_format(sendtime, '%Y')=?", date.getYear() + 1900, StandardBasicTypes.INTEGER))
                    .add(Restrictions.sqlRestriction("date_format(sendtime, '%m')=?", date.getMonth() + 1, StandardBasicTypes.INTEGER))
                    .add(Restrictions.sqlRestriction("date_format(sendtime, '%d')=?", date.getDate(), StandardBasicTypes.INTEGER))
                    .setProjection(Projections.countDistinct("id")).uniqueResult().toString());
            msg.append("您当前使用的是订阅号，每天只能群发 1 条消息，您当天还剩 " + (1 - count) + " 条。");
            result = count < 1;
        } else if (type.equals("fuwu")) {
            // 服务号
            int count = Integer.parseInt(factorySession.createCriteria(WeixinSendrecord.class)
                    .add(Restrictions.sqlRestriction("date_format(sendtime, '%Y')=?", date.getYear() + 1900, StandardBasicTypes.INTEGER))
                    .add(Restrictions.sqlRestriction("date_format(sendtime, '%m')=?", date.getMonth() + 1, StandardBasicTypes.INTEGER))
                    .setProjection(Projections.countDistinct("id")).uniqueResult().toString());
            msg.append("您当前使用的是服务号，每月只能群发 4 条消息，您本月还剩 " + (4 - count) + " 条。");
            result = count < 4;
        }
        return result;
    }

}
